<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>画板</title>
  <style>
      * {
          margin: 0;
          padding: 0;
      }

      .canvas-box {

          box-sizing: border-box;
          width: 100%;
          margin: auto;

          /*display: flex;*/
          /*flex-direction: column;*/
          /*align-items: center;*/
          /*margin-top: 20px;*/
          /*position: relative;*/
      }

      #canvas {
          border: 1px inset lightgray;
          box-sizing: border-box;

      }

      .tools {
          display: flex;
          align-items: center;
          margin-top: 20px;
          align-self: flex-start;
          /*margin: 0 auto;*/
      }

  </style>
</head>
<body>
<div class="canvas-box">
    <canvas id="canvas" height="800"></canvas>
    <div style="" class="tools">
      <div>
        <span>请选择画笔颜色:</span>
        <select id="strokeStyleSelect" style="margin-right: 20px" onchange="handleLineColor(event)">
          <option value="red">red</option>
          <option value="green">green</option>
          <option value="blue">blue</option>
          <option value="orange">orange</option>
        </select>
      </div>
      <div>
        <span>是否开启辅助线:</span>
        <input type="checkbox" id="isShowGuide" onchange="handleGuide(event)">
      </div>
      <div style="margin-left: 20px">
        <span>请选择画笔粗细:</span>
        <select id="lineWidthSelect" onchange="handleLineWidth(event)">
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
          <option value="4">4</option>
        </select>

      </div>
      <div style="margin-left: 20px">
        <span>请选择线段类型:</span>
        <select id="lineTypeSelect" onchange="handleLineType(event)">
          <option value="solid">实线</option>
          <option value="dash">虚线</option>
        </select>
      </div>
      <div style="margin-left: 20px">
        <span>请选择画笔类型:</span>
        <select id="paintbrush" onchange="handlePaintbrush(event)">
          <option value="line">line</option>
          <option value="rect">rect</option>
          <option value="circle">circle</option>
        </select>
      </div>
      <div>
        <span>是否擦除:</span>
        <input type="checkbox" id="isClear" onchange="handleClear(event)">
      </div>
    </div>
    <button style="margin-top: 20px" onclick="clearCanvas()">清空画布</button>

</div>

<script>
  const canvas = document.getElementById('canvas')
  canvas.width = window.outerWidth - 100
  canvasBox = document.querySelector('.canvas-box')
  canvasBox.style.width = canvas.width + 'px'
  const ctx = canvas.getContext('2d')


  class point {
    constructor(x, y, color) {
      this.x = x
      this.y = y
      this.color = color
    }
  }

  let drawingSurfaceImageData = null
  const startPoint = new point(0, 0, 'red')

  let isDraw = false
  let currentColor = 'red'
  let isShowGuide = false
  let lineWidth = 1
  let lineType = 'solid'
  let paintbrushType = 'line'
  let isClear = false
  let lastX =0
  let lastY =0
  const paintbrushMap = {
    line: drawLine,
    rect: drawRect,
    circle: drawCircle
  }

  function drawRect(x, y) {
    ctx.strokeStyle = currentColor
    ctx.lineWidth = lineWidth
    ctx.setLineDash(lineType === 'solid' ? [] : [5, 5])
    const rectObj = {
      width: Math.abs(x - startPoint.x),
      height: Math.abs(y - startPoint.y),
      x: x > startPoint.x ? startPoint.x : x,
      y: y > startPoint.y ? startPoint.y : y
    }
    ctx.beginPath()
    ctx.rect(rectObj.x, rectObj.y, rectObj.width, rectObj.height)
    // ctx.fill()
    ctx.stroke()
  }

  function drawCircle(x, y) {
    ctx.strokeStyle = currentColor
    ctx.lineWidth = lineWidth
    ctx.setLineDash(lineType === 'solid' ? [] : [5, 5])
    const circleObj = {
      radius: Math.sqrt(Math.pow(Math.abs(x - startPoint.x), 2) + Math.pow(Math.abs(y - startPoint.y), 2)),
      x: startPoint.x,
      y: startPoint.y
    }
    ctx.beginPath()
    ctx.arc(circleObj.x, circleObj.y, circleObj.radius, 0, 2 * Math.PI)
    ctx.stroke()
  }


  function handleLineColor(e) {
    currentColor = e.target.value
  }

  function handleGuide(e) {
    isShowGuide = e.target.checked
  }

  function handleLineWidth(e) {
    lineWidth = e.target.value
  }

  function handleLineType(e) {
    lineType = e.target.value
  }

  function handlePaintbrush(e) {
    paintbrushType = e.target.value
  }

  function handleClear(e) {
    isClear = e.target.checked
  }



  function erase() {
    ctx.save()
    ctx.beginPath()
    ctx.rect(lastX,lastY,25,25)
    ctx.clip()
    drawGrid('lightgray', 20, 20)
    ctx.restore()
  }

  function drawEraser(x, y) {
    ctx.save();
    ctx.beginPath();
    ctx.strokeStyle = 'green';
    ctx.lineWidth = 10;
    ctx.rect(x, y, 25, 25);
    ctx.clip()
    ctx.stroke();
    ctx.restore();
  }


  function clearCanvas() {
    ctx.clearRect(0, 0, canvas.width, canvas.height)
    drawGrid('lightgray', 20, 20)
  }


  canvas.onmousedown = (e) => {
    e.preventDefault();
    const {x, y} = windowToCanvas(e.clientX, e.clientY)
    if (!isClear) saveDrawingSurface()
    startPoint.x = x
    startPoint.y = y
    lastX =x
    lastY =y
    isDraw = true
  }
  canvas.onmousemove = (e) => {
    e.preventDefault();
    if (isDraw) {
      const {x, y} = windowToCanvas(e.clientX, e.clientY)
      if (!isClear) {
        restoreDrawingSurface()
        paintbrushMap[paintbrushType](x, y)
        if (isShowGuide) drawGuide(x, y)
      } else {
        erase()
        drawEraser(x, y)
      }

      lastX =x
      lastY =y
    }
  }
  canvas.onmouseup = (e) => {
    e.preventDefault();
    const {x, y} = windowToCanvas(e.clientX, e.clientY)

    if (!isClear) {
      restoreDrawingSurface()
      paintbrushMap[paintbrushType](x, y)
    } else {
      erase()
    }
    isDraw = false
  }

  // 画辅助线
  function drawGuide(x, y) {
    ctx.strokeStyle = 'rgba(0,0,230,0.4)';
    ctx.lineWidth = 0.5;
    // 画y轴
    ctx.beginPath()
    ctx.moveTo(x + 0.5, 0)
    ctx.lineTo(x + 0.5, canvas.height)
    ctx.stroke()
    // 画x轴
    ctx.beginPath()
    ctx.moveTo(0, y + 0.5)
    ctx.lineTo(canvas.width, y + 0.5)
    ctx.stroke()
  }


  //保存画布
  function saveDrawingSurface() {
    drawingSurfaceImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  }

  // 恢复画布
  function restoreDrawingSurface() {
    ctx.putImageData(drawingSurfaceImageData, 0, 0);
  }

  // 画线段
  function drawLine(x, y) {
    ctx.strokeStyle = currentColor
    ctx.lineWidth = lineWidth
    ctx.setLineDash(lineType === 'solid' ? [] : [5, 5])
    ctx.beginPath()
    ctx.moveTo(startPoint.x, startPoint.y)
    ctx.lineTo(x, y)
    ctx.stroke()
  }

  /**
   * 坐标转化为canvas坐标
   * @param x
   * @param y
   */
  function windowToCanvas(x, y) {
    //返回元素的大小以及位置
    const box = canvas.getBoundingClientRect();
    return {x: x - box.left * (canvas.width / box.width), y: y - box.top * (canvas.height / box.height)};

  }


  // 画网格
  function drawGrid(color, stepX, stepY) {
    ctx.save()
    ctx.strokeStyle = 'lightgray';
    ctx.fillStyle = '#ffffff';
    ctx.lineWidth = 0.5;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    for (let i = stepX + 0.5; i < canvas.width; i += stepX) {
      ctx.beginPath()
      ctx.moveTo(i, 0)
      ctx.lineTo(i, canvas.height)
      ctx.stroke()
    }
    for (let i = stepY + 0.5; i < canvas.height; i += stepY) {
      ctx.beginPath()
      ctx.moveTo(0, i)
      ctx.lineTo(ctx.canvas.width, i)
      ctx.stroke()
    }
    ctx.restore()
  }

  drawGrid('lightgray', 20, 20)

</script>
</body>
</html>